#include <cstdio>
#include "env/vl_env.h"
#include "utils/vl_args.h"
#include "utils/vl_utils.h"
#include "support/c/vl_c_include_support.h"
#include "support/vl_generate_support.h"

using namespace VL;

/**
 * @Author: CimZzz
 * @Date: 2022-02-16 15:21:50
 * 
 * 打印错误文本
 */
#define _VL_ERROR(error, arg) printf("\n"); \
	printf("\033[31merror: %s\033[0m\n", error.getMessage().c_str()); \
	printf("\n"); \
	printf("command: %s\n", arg.getFullyCommand().c_str()); \
	printf("\n"); \
	printf("Use \"-h\" to show usage ... \n"); \
	printf("\n"); \
	return 1;

/**
 * @Author: CimZzz
 * @Date: 2022-06-23 17:03:27
 * 
 * 程序异常 TOPIC
 */
#define _TOPIC_MAIN "Prog"

/**
 * @Author: CimZzz
 * @Date: 2022-08-23 11:08:52
 * 
 * 清理指令 TOPIC
 */
#define _TOPIC_CLEAN "Clean"

/**
 * @Author: CimZzz
 * @Date: 2022-02-14 11:48:47
 * 
 * 打印帮助文本
 */
void printHelp() {
	printf(
	"Virtual-Lightning Build Program, ver %s, by CimZzz\n\n"
	"\n"
	"Usage:\n"
	"\n"
	"-h|-help|--help|help               : print help information\n"
	"-v|-version|--version|version      : print version information\n"
	"--print-marco                      : print all inner-marco\n"
	"\n"
	"<Clean Command>\n"
	"\n"
	"clean [project dir]\n"
	"\n"
	"clean all things of building \n"
	"\n"
	"\n"
	"<Generate Command>\n"
	"\n"
	"gen [arguments] [project dir]\n"
	"\n"
	"Arguments:\n"
	"\n"
	"    -l|-lib                    : generate library project(vl-style)\n"
	"    -e|-exec           	    : generate executable project(vl-style)\n"
	"    -c|-class               	: generate a pair of header file and source file\n"
	"    -t|-toolchain           	: generate a toolchain file\n"
	"    -n|-name                	: project name, class name or file name\n"
	"\n"
	"\n"
	"<Config Command>\n"
	"\n"
	"config [arguments] [project dir]\n"
	"\n"
	"Arguments:\n"
	"\n"
	"    -i|-install             : which directory to install\n"
	"    -t|-toolchain           : set toolchain file\n"
	"    -p|-parallel            : number of parallel tasks, default is 1, max is 16\n"
	"    --builddir              : specify which directory save build file (under .vl_build dir)\n"
	"    --all                   : show all configurations, default is true\n"
	"    --options               : show configurations of compile options\n"
	"    --dep                   : show configurations of dependencies\n"
	"    --lib                   : show configurations of libraries\n"
	"    --prog                  : show configurations of program\n"
	"    --test                  : show configurations of test unit\n"
	"    --feature               : show all features if has description\n"
	"    --version               : print project version and return\n"
	"    --vlp-feature           : specify features of configuration file\n"
	"\n"
	"\n"
	"<Dirty Command>\n"
	"\n"
	"dirty [arguments] [project dir]\n"
	"\n"
	"Arguments:\n"
	"\n"
	"    -i|-install             : which directory to install\n"
	"    -t|-toolchain           : set toolchain file\n"
	"    -p|-parallel            : number of parallel tasks, default is 1, max is 16\n"
	"    -v|--verbose            : print all infomation\n"
	"    --builddir              : specify which directory save build file (under .vl_build dir)\n"
	"    --lib                   : show configurations of libraries\n"
	"    --prog                  : show configurations of program\n"
	"    --test                  : show configurations of test unit\n"
	"    --vlp-feature           : specify features of configuration file\n"
	"\n"
	"\n"
	"<Dependency Command>\n"
	"\n"
	"dep [arguments] [project dir]\n"
	"\n"
	"Arguments:\n"
	"\n"
	"    -t|-toolchain           : set toolchain file\n"
	"    -p|-parallel            : number of parallel tasks, default is 1, max is 16\n"
	"    --builddir              : specify which directory save build file (under .vl_build dir)\n"
	"    --only-check            : only load local processed dependencies \n"
	"    --force                 : force re-fetch and reload all dependencies \n"
	"    --check-load            : load local dependencies if exists, if not will try to fetch dependencies\n"
	"    --vlp-feature           : specify features of configuration file\n"
	"\n"
	"\n"
	"<Compile Command>\n"
	"\n"
	"compile [arguments] [project dir]\n"
	"\n"
	"Arguments:\n"
	"\n"
	"    -i|-install             : which directory to install\n"
	"    -t|-toolchain           : set toolchain file\n"
	"    -p|-parallel            : number of parallel tasks, default is 1, max is 16\n"
	"    -v|--verbose            : print all infomation\n"
	"    -vc|--verbose-compile   : print infomation of compile step\n"
	"    -vd|--verbose-dirty     : print infomation of dirty step\n"
	"    --builddir              : specify which directory save build file (under .vl_build dir)\n"
	"    --check-dep             : check dependencies of this project, default not check\n"
	"    --all-type              : all types project will be compiled\n"
	"    --lib                   : compile library\n"
	"    --prog                  : compile program\n"
	"    --test                  : compile test unit\n"
	"    --vlp-feature           : specify features of configuration file\n"
	"    --run-as-root           : use root to run program\n"
	"    --run-prog <prog name>  : compile and run specify program\n"
	"    --run-test <test name>  : compile and run specify test unit\n"
	"    --run-test-select       : compile and run test unit by terminal input\n"
	"    -check|--check-test     : check specify test unit before compile\n"
	"\n"
	" * default compile all type projects\n"
	"\n"
	"\n"
	"<Debug Command>\n"
	"\n"
	"debug [arguments] [file name]\n"
	"\n"
	"Arguments:\n"
	"\n"
	"	 -pc|--prop-vlc <file name>  : print props of vlc file\n"
	"	 -pp|--prop-vlp <file name>  : print props of vlp file\n"
	"	 -ts|--file-ts  <file name>  : check modify time of file\n"
	"\n"
	,
	VLEnv::VERSION);
}

/**
 * @Author: CimZzz
 * @Date: 2022-11-11 18:48:37
 * 
 * 打印全部内置宏
 */
void printAllMarco() {
	printf(
		"VL_PROJ_DIR: 项目目录"
		"\n"
	);
}

/**
 * @Author: CimZzz
 * @Date: 2022-08-21 00:27:43
 * 
 * 解析通用指令参数
 * 
 * @param param 首个指令
 * @param args 指令解析器
 * @param commonParams 通用指令配置指针
 * 
 * @return 如果成功解析返回 true
 */
bool analyzeCommonParams(const std::string& param, VLArgs& args, const std::shared_ptr<CommonConfigParams>& commonParams) {
	if(param == "-t" || param == "-toolchain") {
		if(commonParams->toolchainFile.get() != nullptr) {
			throw VLException(_TOPIC_MAIN, "repeat toolchain file: %s", param.c_str());
		}
		if(args.isEnd()) {
			throw VLException(_TOPIC_MAIN, "must specify an toolchain file");
		}
		auto nextParam = args.nextParam();
		commonParams->toolchainFile = std::shared_ptr<VLFile>(new VLRealPathFile(nextParam));
		return true;
	}
	if(param == "-i" || param == "-install") {
		if(commonParams->installDir.get() != nullptr) {
			throw VLException(_TOPIC_MAIN, "repeat install dir: %s", param.c_str());
		}
		if(args.isEnd()) {
			throw VLException(_TOPIC_MAIN, "must specify an install dir");
		}
		auto nextParam = args.nextParam();
		commonParams->installDir = std::shared_ptr<VLFile>(new VLRealPathFile(nextParam));
		return true;
	}
	if(param == "--builddir") {
		if(commonParams->buildDir.get() != nullptr) {
			throw VLException(_TOPIC_MAIN, "repeat build dir: %s", param.c_str());
		}
		if(args.isEnd()) {
			throw VLException(_TOPIC_MAIN, "must specify an build dir");
		}
		auto nextParam = args.nextParam();
		commonParams->buildDir.reset(new std::string(nextParam));
		return true;
	}
	if(param == "-p" || param == "-parallel") {
		if(commonParams->parallel != 0) {
			throw VLException(_TOPIC_MAIN, "repeat parallel count: %s", param.c_str());
		}
		auto nextParam = args.nextParam();
		auto count = VLUtils::str2int(nextParam);
		if(count.get() == nullptr || *count.get() <= 0) {
			throw VLException(_TOPIC_MAIN, "invalid parallel count, must be an positive integer and more than 0: %s", nextParam.c_str());
		}

		commonParams->parallel = *(count.get());
		return true;
	}
	if(param == "--vlp-feature") {
		if(args.isEnd()) {
			throw VLException(_TOPIC_MAIN, "must specify some feature");
		}
		auto nextParam = args.nextParam();
		std::shared_ptr<VLPFeature> featurePtr(new VLPFeature);
		while(true) {
			int spaceIndex = nextParam.find_first_of(' ');
			if(spaceIndex == -1) {
				featurePtr->nameSet.emplace(nextParam);
				break;
			}
			featurePtr->nameSet.emplace(nextParam.substr(0, spaceIndex));
			nextParam.erase(0, spaceIndex + 1);
		}
		commonParams->vlpFeature = featurePtr;
		
		return true;
	}
	if(param == "-v" || param == "--verbose") {
		commonParams->verbose = CommonConfigParams::VerboseType::VT_All;
		return true;
	}
	if(param == "-vc" || param == "--verbose-compile") {
		commonParams->verbose = CommonConfigParams::VerboseType::VT_Compile;
		return true;
	}
	if(param == "-vd" || param == "--verbose-dirty") {
		commonParams->verbose = CommonConfigParams::VerboseType::VT_Dirty;
		return true;
	}

	return false;
}

int main(int argc, const char ** argv) {
	VLArgs args(argc, argv);
	// 获取主 ENV 环境
	VLEnv env;

	auto command = args.nextParam();
	try {
		if(command == "-h" || command == "-help" || command == "--help" || command == "help") {
			printHelp();
			return 0;
		}
		if(command == "-v" || command == "--version" || command == "-version" || command == "version") {
			printf("Virtual-Lightning Build Program, ver %s, by CimZzz\n", VLEnv::VERSION);
			return 0;
		}
		if(command == "--print-marco") {
			printAllMarco();
			return 0;
		}
		if(command == "clean") {
			// 编译步骤
			while(!args.isEnd()) {
				auto param = args.nextParam();

				if(args.isEnd()) {
					// 最后一个参数
					env.projectDir = std::shared_ptr<VLFile>(new VLRealPathFile(param));
					break;
				} else {
					throw VLException(_TOPIC_MAIN, "unknown param: %s", param.c_str());
				}
			}

			if(env.projectDir.get() == nullptr) {
				// 没有选中生成文件夹, 则指定到当前目录
				env.projectDir = std::shared_ptr<VLFile>(new VLRealPathFile("."));
			}
			
			env.initProject();
			if(env.projectProperties->isProjectExist) {
				if(env.projectProperties->buildDir.isExist()) {
					printf("delete dir %s ...\n", env.projectProperties->buildDir.getAbsolutePathStr());
					if(!env.projectProperties->buildDir.deleteFile()) {
						throw VLException(_TOPIC_CLEAN, "delete dir %s failed", env.projectProperties->buildDir.getAbsolutePathStr());
					}
				}
			}
			return 0;
		}
		if(command == "gen") {
			std::shared_ptr<GenerateParams> generateParam(new GenerateParams());
			env.generateParams = generateParam;
			// 生成步骤
			while(!args.isEnd()) {
				auto param = args.nextParam();
				
				if(param == "-l" || param == "-lib") {
					if(generateParam->generateType != GenerateParams::None) {
						throw VLException(_TOPIC_MAIN, "repeat generate type: %s", param.c_str());
					}
					generateParam->generateType = GenerateParams::Library;
					continue;
				}
				if(param == "-t" || param == "-toolchain") {
					if(generateParam->generateType != GenerateParams::None) {
						throw VLException(_TOPIC_MAIN, "repeat generate type: %s", param.c_str());
					}
					generateParam->generateType = GenerateParams::Toolchain;
					continue;
				}
				if(param == "-e" || param == "-exec") {
					if(generateParam->generateType != GenerateParams::None) {
						throw VLException(_TOPIC_MAIN, "repeat generate type: %s", param.c_str());
					}
					generateParam->generateType = GenerateParams::Exec;
					continue;
				}
				if(param == "-c" || param == "-class") {
					if(generateParam->generateType != GenerateParams::None) {
						throw VLException(_TOPIC_MAIN, "repeat generate type: %s", param.c_str());
					}
					generateParam->generateType = GenerateParams::Class;
					continue;
				}
				if(param == "-n") {
					generateParam->name = args.nextParam();
					continue;
				}

				if(args.isEnd()) {
					// 最后一个参数
					env.projectDir = std::shared_ptr<VLFile>(new VLRealPathFile(param));
					break;
				} else {
					throw VLException(_TOPIC_MAIN, "unknown param: %s", param.c_str());
				}
			}

			if(env.projectDir.get() == nullptr) {
				// 没有选中生成文件夹, 则指定到当前目录
				env.projectDir = std::shared_ptr<VLFile>(new VLRealPathFile("."));
			}

			GenerateSupport support;
			support.doAction(&env);
			return 0;
		}
		if(command == "config") {
			std::shared_ptr<ConfigureParams> configureParams(new ConfigureParams());
			configureParams->ignoreConfigError = true;
			env.configureParams = configureParams;
			// 编译步骤
			while(!args.isEnd()) {
				auto param = args.nextParam();
				if(analyzeCommonParams(param, args, env.commonParams)) {
					continue;
				}

				if(param == "--all") {
					configureParams->configOptions |= ConfigureParams::ConfigOptions::CO_ALL;
					continue;
				}

				if(param == "--options") {
					configureParams->configOptions |= ConfigureParams::ConfigOptions::CO_CompileOption;
					continue;
				}

				if(param == "--dep") {
					configureParams->configOptions |= ConfigureParams::ConfigOptions::CO_ReadDependencies;
					continue;
				}

				if(param == "--lib") {
					configureParams->configOptions |= ConfigureParams::ConfigOptions::CO_CompileLibrary;
					continue;
				}

				if(param == "--prog") {
					configureParams->configOptions |= ConfigureParams::ConfigOptions::CO_CompileExecutable;
					continue;
				}

				if(param == "--test") {
					configureParams->configOptions |= ConfigureParams::ConfigOptions::CO_CompileTest;
					continue;
				}

				if(param == "--feature") {
					configureParams->configOptions = ConfigureParams::ConfigOptions::CO_ShowFeature;
					continue;
				}

				if(param == "--version") {
					configureParams->configOptions = ConfigureParams::ConfigOptions::CO_PrintVersion;
					continue;
				}

				if(args.isEnd()) {
					// 最后一个参数
					env.projectDir = std::shared_ptr<VLFile>(new VLRealPathFile(param));
					break;
				} else {
					throw VLException(_TOPIC_MAIN, "unknown param: %s", param.c_str());
				}
			}

			if(env.projectDir.get() == nullptr) {
				// 没有选中生成文件夹, 则指定到当前目录
				env.projectDir = std::shared_ptr<VLFile>(new VLRealPathFile("."));
			}
			
			if(configureParams->configOptions == ConfigureParams::ConfigOptions::CO_None) {
				// 缺省配置全部
				configureParams->configOptions = ConfigureParams::ConfigOptions::CO_ALL;
			}

			CConfigSupport support;
			support.doAction(&env);
			return 0;
		}

		if(command == "dirty") {
			std::shared_ptr<DirtyParams> dirtyParams(new DirtyParams());
			env.dirtyParams = dirtyParams;
			// 编译步骤
			while(!args.isEnd()) {
				auto param = args.nextParam();
				if(analyzeCommonParams(param, args, env.commonParams)) {
					continue;
				}

				if(param == "--lib") {
					dirtyParams->dirtyOptions |= ConfigureParams::ConfigOptions::CO_CompileLibrary;
					continue;
				}

				if(param == "--prog") {
					dirtyParams->dirtyOptions |= ConfigureParams::ConfigOptions::CO_CompileExecutable;
					continue;
				}

				if(param == "--test") {
					dirtyParams->dirtyOptions |= ConfigureParams::ConfigOptions::CO_CompileTest;
					continue;
				}

				if(args.isEnd()) {
					// 最后一个参数
					env.projectDir = std::shared_ptr<VLFile>(new VLRealPathFile(param));
					break;
				} else {
					throw VLException(_TOPIC_MAIN, "unknown param: %s", param.c_str());
				}
			}


			if(env.projectDir.get() == nullptr) {
				// 没有选中生成文件夹, 则指定到当前目录
				env.projectDir = std::shared_ptr<VLFile>(new VLRealPathFile("."));
			}

			if(dirtyParams->dirtyOptions == 0) {
				dirtyParams->dirtyOptions = ConfigureParams::ConfigOptions::CO_ALL;
			}
			
			CDirtySupport support;
			support.doAction(&env);
			return 0;
		}

		if(command == "dep") {
			std::shared_ptr<DependencyParams> dependencyParams(new DependencyParams());
			env.dependencyParams = dependencyParams;
			// 编译步骤
			while(!args.isEnd()) {
				auto param = args.nextParam();
				if(analyzeCommonParams(param, args, env.commonParams)) {
					continue;
				}

				if(param == "--only-check") {
					dependencyParams->depOptions |= DependencyParams::DepOptions::DO_OnlyCheckDep;
					continue;
				}

				if(param == "--force") {
					dependencyParams->depOptions |= DependencyParams::DepOptions::DO_ForceCheckDep;
					continue;
				}

				if(param == "--check-load") {
					dependencyParams->depOptions |= DependencyParams::DepOptions::DO_CheckOrLoad;
					continue;
				}

				if(args.isEnd()) {
					// 最后一个参数
					env.projectDir = std::shared_ptr<VLFile>(new VLRealPathFile(param));
					break;
				} else {
					throw VLException(_TOPIC_MAIN, "unknown param: %s", param.c_str());
				}
			}

			if(env.projectDir.get() == nullptr) {
				// 没有选中生成文件夹, 则指定到当前目录
				env.projectDir = std::shared_ptr<VLFile>(new VLRealPathFile("."));
			}

			if(dependencyParams->depOptions == 0) {
				dependencyParams->depOptions = DependencyParams::DepOptions::DO_ForceCheckDep;
			}
			
			CDepSupport support;
			support.doAction(&env);
			return 0;
		}
		
		if(command == "compile") {
			std::shared_ptr<CompileParams> compileParam(new CompileParams());
			env.compileParam = compileParam;
			// 编译步骤
			while(!args.isEnd()) {
				auto param = args.nextParam();
				if(analyzeCommonParams(param, args, env.commonParams)) {
					continue;
				}

				if(param == "--check-dep") {
					compileParam->checkDep = true;
					continue;
				}

				if(param == "-check" || param == "--check-test") {
					compileParam->checkTest = true;
					compileParam->compileOption |= CompileParams::CompileOptions::CO_TEST;
					continue;
				}

				if(param == "--run-as-root") {
					compileParam->runAsRoot = true;
					continue;
				}
				
				if(param == "--all-type") {
					compileParam->compileOption |= CompileParams::CompileOptions::CO_ALL;
					continue;
				}
				
				if(param == "--lib") {
					compileParam->compileOption |= CompileParams::CompileOptions::CO_LIB;
					continue;
				}
				
				if(param == "--prog") {
					compileParam->compileOption |= CompileParams::CompileOptions::CO_EXEC;
					continue;
				}
				
				if(param == "--test") {
					compileParam->compileOption |= CompileParams::CompileOptions::CO_TEST;
					continue;
				}
				
				if(param == "--run-test-select") {
					compileParam->runTestSelect = true;
					continue;
				}

				if(param == "--run-prog") {
					if(compileParam->runTestSelect) {
						throw VLException(_TOPIC_MAIN, "\"--run-prog\" cannot compatible with \"--run-test-select\"");
					}
					if(!compileParam->runTestName.empty() || !compileParam->runProgName.empty()) {
						throw VLException(_TOPIC_MAIN, "repeat run program : %s", param.c_str());
					}
					auto nextParam = args.nextParam();
					compileParam->runProgName = nextParam;
					continue;
				}

				if(param == "--run-test") {
					if(compileParam->runTestSelect) {
						throw VLException(_TOPIC_MAIN, "\"--run-test\" cannot compatible with \"--run-test-select\"");
					}
					if(!compileParam->runTestName.empty() || !compileParam->runProgName.empty()) {
						throw VLException(_TOPIC_MAIN, "repeat run test unit : %s", param.c_str());
					}
					auto nextParam = args.nextParam();
					compileParam->runTestName = nextParam;
					continue;
				}

				if(args.isEnd()) {
					// 最后一个参数
					env.projectDir = std::shared_ptr<VLFile>(new VLRealPathFile(param));
					break;
				} else {
					throw VLException(_TOPIC_MAIN, "unknown param: %s", param.c_str());
				}
			}


			if(env.projectDir.get() == nullptr) {
				// 没有选中生成文件夹, 则指定到当前目录
				env.projectDir = std::shared_ptr<VLFile>(new VLRealPathFile("."));
			}

			if(compileParam->compileOption != CompileParams::CompileOptions::CO_ALL && (compileParam->compileOption & CompileParams::CompileOptions::CO_TEST) == 0 && compileParam->runTestSelect) {
				throw VLException(_TOPIC_MAIN, "\"--run-test-select\" must choose to compile test unit");
			}
			
			CCompileSupport support;
			support.doAction(&env);
			return 0;
		}


		#ifdef _VL_BUILD_DEBUG
		
		if(command == "debug") {
			VLPFeature feature;

			// 编译步骤
			while(!args.isEnd()) {
				auto param = args.nextParam();
				if(param == "-pp" || param == "--prop-vlp") {
					if(args.isEnd()) {
						throw VLException(_TOPIC_MAIN, "must specify a prop file");
					}
					param = args.nextParam();
					if(!args.isEnd()) {
						throw VLException(_TOPIC_MAIN, "wrong debug params");
					}
					VLRealPathFile propFile(param);
					RootProp prop;
					if(!prop.readFromVLPFile(propFile, true, &feature)) {
						throw VLException(_TOPIC_MAIN, "read file failed.");
					}
					prop.print();

					break;
				}
				if(param == "-f" || param == "--vlp-feature") {
					if(args.isEnd()) {
						throw VLException(_TOPIC_MAIN, "must specify some feature");
					}
					param = args.nextParam();
					while(true) {
						int spaceIndex = param.find_first_of(' ');
						if(spaceIndex == -1) {
							feature.nameSet.emplace(param);
							break;
						}
						feature.nameSet.emplace(param.substr(0, spaceIndex));
						param.erase(0, spaceIndex + 1);
					}
					continue;
				}
				if(param == "-pc" || param == "--prop-vlc") {
					if(args.isEnd()) {
						throw VLException(_TOPIC_MAIN, "must specify a prop file");
					}
					param = args.nextParam();
					if(!args.isEnd()) {
						throw VLException(_TOPIC_MAIN, "wrong debug params");
					}
					VLRealPathFile propFile(param);
					RootProp prop;
					if(!prop.readFromVLCFile(propFile)) {
						throw VLException(_TOPIC_MAIN, "read file failed.");
					}
					prop.print();

					break;
				}
				if(param == "-ts" || param == "--file-ts") {
					if(args.isEnd()) {
						throw VLException(_TOPIC_MAIN, "must specify a file");
					}
					param = args.nextParam();
					if(!args.isEnd()) {
						throw VLException(_TOPIC_MAIN, "wrong debug params");
					}
					VLRealPathFile propFile(param);
					
					printf("%s\n", VLUtils::long2str(propFile.getModifyTime()).c_str());
					break;
				}
			}
			return 0;
		}
		

		#endif //_VL_BUILD_DEBUG
		
			
		throw VLException(_TOPIC_MAIN, "unknown command");
	} catch(const VLException& e) {
		// 清空全部任务
		_VL_ERROR(e, args);
	}

	return 0;
}
